<template><div><h2 id="线程" tabindex="-1"><a class="header-anchor" href="#线程"><span>线程</span></a></h2>
<p>线程是CPU任务调度的最小执行单位，每个线程拥有自己独立的程序计数器、虚拟机栈、本地方法栈。
JDK1.2及以后，Java线程改为基于原生线程（Native Threads）实现，也就是说JVM直接使用操作系统原生的内核级线程<code v-pre>（内核线程）</code>来实现Java线程，有操作系统内核进行线程的调度和管理。<strong>（现在的Java线程的本质其实就是操作系统的线程）</strong></p>
<p>需要简单了解的两个概念：</p>
<ul>
<li>用户线程：由用户空间程序管理和调度的线程，运行在用户空间（专门给应用程序使用，创建和切换成本低，但不可以利用多核）。</li>
<li>内核线程：由操作系统内核管理和调度的线程，运行在内核空间（只有内核程序可以访问，创建和切换成本高，可以利用多核）。</li>
</ul>
<p>Java线程采用的是一对一的线程模型，简单来说就是一个Java线程对应一个系统内核线程。</p>
<p>JVM运行时内存：</p>
<ul>
<li>线程私有：程序计数器、虚拟机栈、本地方法栈</li>
<li>线程共享：堆、方法区</li>
</ul>
<p>每个线程都有自己的程序计数器、本地方法栈、虚拟机栈。共享者进程的堆和方法区（JDK1.8之后的元空间）资源。</p>
<p><strong>程序计数器：</strong> 主要为了记录在线程来回切换时，程序具体执行到哪一步的位置，方便继续往下执行。</p>
<p><strong>虚拟机栈：</strong> 在执行方法时创建一个栈帧，用于存储局部变量、常量池引用等信息。从<code v-pre>方法调用-&gt;方法执行完成</code>的过程就对应着一个栈帧在Java虚拟机栈中<code v-pre>入栈和出栈</code>的过程。</p>
<p><strong>本地方法栈：</strong> 本地方法栈记录Java虚拟机使用到的<code v-pre>Navite方法</code>服务。</p>
<p>为了保证线程中的局部变量不被别的线程访问到，所以虚拟机栈和本地方法栈是线程私有的。</p>
<h4 id="线程的生命周期和状态" tabindex="-1"><a class="header-anchor" href="#线程的生命周期和状态"><span>线程的生命周期和状态</span></a></h4>
<ul>
<li>New：初始状态，线程被创建出来但还没有被调用<code v-pre>start()</code>（准备就绪）。</li>
<li>RUNNABLE：运行状态。线程被调用了<code v-pre>run()</code>，线程处于运行的状态。</li>
<li>BLOCKED：阻塞状态，需要等待锁释放后方可继续运行。</li>
<li>WAITING：等待状态，表示该线程需要等待其他线程作出一些特定的动作（通知或中断）。</li>
<li>TIME_WAITING：超时等待状态，可以在指定的时间后自行返回而不是像WAITING那样一直等待（睡眠）。</li>
<li>TERMINATED：终止状态，表示该线程已经运行完毕。
<img src="@source/img/threads-state.png" alt="alt text" loading="lazy"></li>
</ul>
<p>Thread#sleep() 方法和 Object#wait() 方法对比
共同点：两者都可以暂停线程的执行。
区别：</p>
<ul>
<li>sleep() 方法没有释放锁，而 wait() 方法释放了锁 。</li>
<li>wait() 通常被用于线程间交互/通信，sleep()通常被用于暂停执行。</li>
<li>wait() 方法被调用后，线程不会自动苏醒，需要别的线程调用同一个对象上的 notify()或者 notifyAll() 方法。</li>
<li>sleep()方法执行完成后，线程会自动苏醒，或者也可以使用 wait(long timeout) 超时后线程会自动苏醒。</li>
<li>sleep() 是 Thread 类的静态本地方法，wait() 则是 Object 类的本地方法。</li>
</ul>
<h2 id="多线程" tabindex="-1"><a class="header-anchor" href="#多线程"><span>多线程</span></a></h2>
<h4 id="并发和并行的区别" tabindex="-1"><a class="header-anchor" href="#并发和并行的区别"><span>并发和并行的区别</span></a></h4>
<ul>
<li><strong>并发：</strong> 两个及两个以上的任务在<strong>同一时间段内</strong> 执行。（时间范围）</li>
<li><strong>并行：</strong> 两个及两个以上的任务在<strong>同一时刻</strong> 执行。（瞬间）</li>
</ul>
<h4 id="使用多线程可能带来什么问题" tabindex="-1"><a class="header-anchor" href="#使用多线程可能带来什么问题"><span>使用多线程可能带来什么问题?</span></a></h4>
<p>并发编程的目的就是为了能提高程序的执行效率进而提高程序的运行速度，但是并发编程并不总是能提高程序运行速度的，而且并发编程可能会遇到很多问题，比如：内存泄漏、死锁、线程不安全等等。</p>
<h2 id="死锁" tabindex="-1"><a class="header-anchor" href="#死锁"><span>死锁</span></a></h2>
<p>多个线程同时被阻塞，它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞，因此程序不可能正常终止。
例如：线程 A 持有资源 2，线程 B 持有资源 1，他们同时都想申请对方的资源，所以这两个线程就会互相等待而进入死锁状态。</p>
<h4 id="产生死锁的四个必要条件" tabindex="-1"><a class="header-anchor" href="#产生死锁的四个必要条件"><span>产生死锁的四个必要条件：</span></a></h4>
<ul>
<li><strong>互斥条件：</strong> 该资源<code v-pre>任意一个时刻只有一个线程占用</code>。</li>
<li><strong>请求和保持条件：</strong> 一个线程因请求资源而阻塞时，<code v-pre>不释放已获得的资源</code>。</li>
<li><strong>不剥夺条件：</strong> 线程已获得的资源在未使用完之前<code v-pre>不能被其他线程强行剥夺</code>，只能是使用完毕后主动释放。</li>
<li><strong>循环等待条件：</strong> 若干线程之间形成一种头尾相接的<code v-pre>循环等待资源</code>关系。</li>
</ul>
<h4 id="如何检测死锁" tabindex="-1"><a class="header-anchor" href="#如何检测死锁"><span>如何检测死锁？</span></a></h4>
<ul>
<li>使用<code v-pre>jmap</code>、<code v-pre>jstack</code>等命令查看JVM线程的栈和堆内存的情况。如果有死锁，<code v-pre>jstack</code>的输出中通常会有<code v-pre>Found one Java-level deadlock</code>的字样，后面会紧跟着死锁相关的线程信息。另外，实际项目中还可以搭配使用<code v-pre>top</code>、<code v-pre>df</code>、<code v-pre>free</code>等命令查看操作系统的基本情况，出现死锁可能会导致 CPU、内存等资源消耗过高。</li>
<li>采用 VisualVM、JConsole 等工具进行排查。</li>
</ul>
<h4 id="如何预防和避免线程死锁" tabindex="-1"><a class="header-anchor" href="#如何预防和避免线程死锁"><span>如何预防和避免线程死锁?</span></a></h4>
<p><strong>解法：破坏死锁的产生的必要条件即可。</strong></p>
<ul>
<li><strong>破坏请求与保持条件：</strong> 一次性申请所有的资源。</li>
<li><strong>破坏不剥夺条件：</strong> 占用部分资源的线程进一步申请其他资源时，如果申请不到，可以主动释放它占有的资源。</li>
<li><strong>破坏循环等待条件：</strong> 靠按序申请资源来预防。按某一顺序申请资源，释放资源则反序释放。破坏循环等待条件。</li>
</ul>
<p><strong>如何避免死锁？</strong>
避免死锁就是在资源分配时，借助于算法（比如银行家算法）对资源分配进行计算评估，使其进入安全状态。</p>
<blockquote>
<p><strong>安全状态:</strong> 指的是系统能够按照某种线程推进顺序（P1、P2、P3……Pn）来为每个线程分配所需资源，直到满足每个线程对资源的最大需求，使每个线程都可顺利完成。称 &lt;P1、P2、P3.....Pn&gt; 序列为安全序列。</p>
</blockquote>
<h2 id="虚拟线程" tabindex="-1"><a class="header-anchor" href="#虚拟线程"><span>虚拟线程</span></a></h2>
<p>这是Java21正式发布的，这是一项重量级的更新。后续再说。</p>
<h2 id="jmm内存模型" tabindex="-1"><a class="header-anchor" href="#jmm内存模型"><span>JMM内存模型</span></a></h2>
<h2 id="volatile关键字" tabindex="-1"><a class="header-anchor" href="#volatile关键字"><span>volatile关键字</span></a></h2>
<p><strong>作用：</strong></p>
<ol>
<li>保证变量的可见性。
用<code v-pre>volatile</code>关键字声明变量，可以保证该变量<strong>在多线程中的可见性</strong> ，<code v-pre>实际值存放在主存</code>中，每个线程的本地内存只是保存着<code v-pre>共享变量的副本</code>（共享且不稳定）。</li>
<li>防止JVM的指令重排序。
当将变量用<code v-pre>volatile</code>关键字声明时，在对这个变量进行<strong>读写</strong>操作的时候，会通过插入特定的<strong>内存屏障</strong>的方式来执行指令重排序。</li>
</ol>
<h2 id="父子线程之间如何共享传递数据" tabindex="-1"><a class="header-anchor" href="#父子线程之间如何共享传递数据"><span>父子线程之间如何共享传递数据</span></a></h2>
<ol>
<li>
<p><strong>ThreadLocal</strong>
<code v-pre>ThreadLocal</code>是一种特殊的Java对象，它为每个线程提供了独立的变量副本。通过<code v-pre>ThreadLocal</code>可以实现线程范围内的变量隔离，但不直接用于父子线程共享。不过，父线程可以在线程启动之前将数据放到<code v-pre>ThreadLocal</code>，这样子线程就可以读取到这些数据。</p>
</li>
<li>
<p><strong>使用Concurrent Collections</strong>
Java提供了一些线程安全的集合类，如ConcurrentHashMap、CopyOnWriteArrayList等，可以安全并发地访问和修改该，适用于需要较多线程共享数据的场景。</p>
</li>
<li>
<p><strong>使用消息队列</strong>
在复杂的多线程环境下，使用Java的阻塞队列（如<code v-pre>BlockingQueue</code>）可以在父子线程之间传递数据，并控制线程的执行顺序。</p>
</li>
</ol>
<h2 id="java死锁问题如何解决" tabindex="-1"><a class="header-anchor" href="#java死锁问题如何解决"><span>Java死锁问题如何解决</span></a></h2>
<h2 id="synchronized是如何实现线程同步的" tabindex="-1"><a class="header-anchor" href="#synchronized是如何实现线程同步的"><span>Synchronized是如何实现线程同步的</span></a></h2>
<h2 id="java中如何安全地停止线程" tabindex="-1"><a class="header-anchor" href="#java中如何安全地停止线程"><span>Java中如何安全地停止线程</span></a></h2>
<p><strong>使用标志位停止线程</strong></p>
<h2 id="synchronized和reentrantlock的区别" tabindex="-1"><a class="header-anchor" href="#synchronized和reentrantlock的区别"><span>Synchronized和ReentrantLock的区别</span></a></h2>
<h2 id="java中读写锁的应用场景是什么" tabindex="-1"><a class="header-anchor" href="#java中读写锁的应用场景是什么"><span>Java中读写锁的应用场景是什么</span></a></h2>
<h2 id="什么是阻塞队列以及应用场景" tabindex="-1"><a class="header-anchor" href="#什么是阻塞队列以及应用场景"><span>什么是阻塞队列以及应用场景</span></a></h2>
<h2 id="jdk21中的虚拟线程是怎么回事" tabindex="-1"><a class="header-anchor" href="#jdk21中的虚拟线程是怎么回事"><span>JDK21中的虚拟线程是怎么回事</span></a></h2>
<h2 id="说说cas的实现与原理" tabindex="-1"><a class="header-anchor" href="#说说cas的实现与原理"><span>说说CAS的实现与原理</span></a></h2>
<h2 id="什么是伪共享问题以及如何解决" tabindex="-1"><a class="header-anchor" href="#什么是伪共享问题以及如何解决"><span>什么是伪共享问题以及如何解决</span></a></h2>
<h2 id="future和completablefuture的区别" tabindex="-1"><a class="header-anchor" href="#future和completablefuture的区别"><span>Future和CompletableFuture的区别</span></a></h2>
<h2 id="说说线程本地变量threadlocal及其用法" tabindex="-1"><a class="header-anchor" href="#说说线程本地变量threadlocal及其用法"><span>说说线程本地变量ThreadLocal及其用法</span></a></h2>
<h2 id="说说fork-join框架-与传统线程池有何不同" tabindex="-1"><a class="header-anchor" href="#说说fork-join框架-与传统线程池有何不同"><span>说说Fork/Join框架，与传统线程池有何不同</span></a></h2>
<h2 id="线程数设定成多少更合适" tabindex="-1"><a class="header-anchor" href="#线程数设定成多少更合适"><span>线程数设定成多少更合适</span></a></h2>
<h2 id="线程调用2次start会怎样" tabindex="-1"><a class="header-anchor" href="#线程调用2次start会怎样"><span>线程调用2次start会怎样</span></a></h2>
<h2 id="说说并发和并行的区别" tabindex="-1"><a class="header-anchor" href="#说说并发和并行的区别"><span>说说并发和并行的区别</span></a></h2>
<h2 id="说说线程和进行的区别" tabindex="-1"><a class="header-anchor" href="#说说线程和进行的区别"><span>说说线程和进行的区别</span></a></h2>
<h2 id="java守护线程和本地线程的去呗" tabindex="-1"><a class="header-anchor" href="#java守护线程和本地线程的去呗"><span>Java守护线程和本地线程的去呗</span></a></h2>
<h2 id="启动线程为什么是调用start-而不是run-方法" tabindex="-1"><a class="header-anchor" href="#启动线程为什么是调用start-而不是run-方法"><span>启动线程为什么是调用start()，而不是run()方法？</span></a></h2>
<h2 id="java线程之间是如何通信的" tabindex="-1"><a class="header-anchor" href="#java线程之间是如何通信的"><span>Java线程之间是如何通信的</span></a></h2>
<p>共享变量和内置的同步机制</p>
<h2 id="java中用到的线程调度算法是什么" tabindex="-1"><a class="header-anchor" href="#java中用到的线程调度算法是什么"><span>Java中用到的线程调度算法是什么</span></a></h2>
<h2 id="死锁与活锁-死锁与饥饿的区别" tabindex="-1"><a class="header-anchor" href="#死锁与活锁-死锁与饥饿的区别"><span>死锁与活锁，死锁与饥饿的区别</span></a></h2>
<h2 id="什么情况线程会进入waiting状态" tabindex="-1"><a class="header-anchor" href="#什么情况线程会进入waiting状态"><span>什么情况线程会进入WAITING状态</span></a></h2>
<h2 id="为什么wait和notify要在同步块中调用" tabindex="-1"><a class="header-anchor" href="#为什么wait和notify要在同步块中调用"><span>为什么wait和notify要在同步块中调用</span></a></h2>
<h2 id="线程池中核心线程数量大小怎么设置" tabindex="-1"><a class="header-anchor" href="#线程池中核心线程数量大小怎么设置"><span>线程池中核心线程数量大小怎么设置</span></a></h2>
<h2 id="如何确保线程安全" tabindex="-1"><a class="header-anchor" href="#如何确保线程安全"><span>如何确保线程安全</span></a></h2>
<h2 id="什么是可重入锁" tabindex="-1"><a class="header-anchor" href="#什么是可重入锁"><span>什么是可重入锁</span></a></h2>
<h2 id="锁的优化机制了解吗" tabindex="-1"><a class="header-anchor" href="#锁的优化机制了解吗"><span>锁的优化机制了解吗</span></a></h2>
<p>锁的优化旨在减少锁的竞争，从而提高程序的性能。</p>
<h2 id="什么是不可变对象-对写并发有什么帮助" tabindex="-1"><a class="header-anchor" href="#什么是不可变对象-对写并发有什么帮助"><span>什么是不可变对象，对写并发有什么帮助</span></a></h2>
<h2 id="说说你对jmm内存模型的理解" tabindex="-1"><a class="header-anchor" href="#说说你对jmm内存模型的理解"><span>说说你对JMM内存模型的理解</span></a></h2>
<h2 id="说说对aqs的理解" tabindex="-1"><a class="header-anchor" href="#说说对aqs的理解"><span>说说对AQS的理解</span></a></h2>
<h2 id="reentrantlock中的公平锁和飞公平锁的底层实现" tabindex="-1"><a class="header-anchor" href="#reentrantlock中的公平锁和飞公平锁的底层实现"><span>ReentrantLock中的公平锁和飞公平锁的底层实现</span></a></h2>
<h2 id="java中怎么唤醒一个阻塞的线程" tabindex="-1"><a class="header-anchor" href="#java中怎么唤醒一个阻塞的线程"><span>Java中怎么唤醒一个阻塞的线程</span></a></h2>
<h2 id="什么是线程调度器和时间分片" tabindex="-1"><a class="header-anchor" href="#什么是线程调度器和时间分片"><span>什么是线程调度器和时间分片</span></a></h2>
<h2 id="说说你对volatile的理解" tabindex="-1"><a class="header-anchor" href="#说说你对volatile的理解"><span>说说你对volatile的理解</span></a></h2>
<h2 id="jdk7和jdk8之间hashmap的区别" tabindex="-1"><a class="header-anchor" href="#jdk7和jdk8之间hashmap的区别"><span>JDK7和JDK8之间HashMap的区别</span></a></h2>
<h2 id="或说concurrenthashmap和hashtable的异同点" tabindex="-1"><a class="header-anchor" href="#或说concurrenthashmap和hashtable的异同点"><span>或说ConcurrentHashMap和Hashtable的异同点</span></a></h2>
<h2 id="线程池中线程复用原理" tabindex="-1"><a class="header-anchor" href="#线程池中线程复用原理"><span>线程池中线程复用原理</span></a></h2>
<h2 id="线程池的底层工作原理" tabindex="-1"><a class="header-anchor" href="#线程池的底层工作原理"><span>线程池的底层工作原理</span></a></h2>
<h2 id="线程池的哪些参数影响性能" tabindex="-1"><a class="header-anchor" href="#线程池的哪些参数影响性能"><span>线程池的哪些参数影响性能</span></a></h2>
<h2 id="如何优化线程池的性能" tabindex="-1"><a class="header-anchor" href="#如何优化线程池的性能"><span>如何优化线程池的性能</span></a></h2>
<h2 id="completablefuture异步编排" tabindex="-1"><a class="header-anchor" href="#completablefuture异步编排"><span>CompletableFuture异步编排</span></a></h2>
<h2 id="callable和runnable接口有什么区别" tabindex="-1"><a class="header-anchor" href="#callable和runnable接口有什么区别"><span>Callable和Runnable接口有什么区别</span></a></h2>
<h2 id="如果不允许线程池丢弃任务-应该选择那个拒绝策略" tabindex="-1"><a class="header-anchor" href="#如果不允许线程池丢弃任务-应该选择那个拒绝策略"><span>如果不允许线程池丢弃任务，应该选择那个拒绝策略</span></a></h2>
<ol>
<li>用CallerRunsPolicy，它会采用主线程执行任务，但如果任何非常耗时会阻塞主线程，在高并发场景慎用！</li>
<li>将任务进行持久化，可以用mysql、redis或者mq异步等方案持久化，后续再对任务进行补偿执行。</li>
<li>可以参考Netty的做法，他会在创建一个新的异步线程处理任务。</li>
<li>也可以参考ActiveMQ：再次插入阻塞队列，会加入等待时间，尽可能的保证执行。</li>
<li>最结合系统资源运行情况，可以调整最大线程数和阻塞队列的长度，减少拒绝策略触发。</li>
</ol>
<h2 id="线程池中线程异常后-销毁还是复用" tabindex="-1"><a class="header-anchor" href="#线程池中线程异常后-销毁还是复用"><span>线程池中线程异常后：销毁还是复用？</span></a></h2>
<ul>
<li>当执行方式是execute时，当执行任务出现异常，内部抛出异常，进而就会导致线程中断而被移除掉，并且会创建一个新的线程填充到线程池中。</li>
<li>当执行方式是submit时，内部会把异常捕获住且不会抛出，所以就不会线程移除掉，也不会创建新的线程放入线程池中。
这两种执行方式，都不会影响线程池里面其他线程的正常执行。</li>
</ul>
</div></template>


